<?php

namespace App\Libraries;

class Douyin
{
    /**
     * 抖店开发平台类
     * 官方文档：https://op.jinritemai.com/docs/api-docs/13/1511
     */
    const API_URL_PREFIX = 'https://openapi-fxg.jinritemai.com';

    private $wx_mini_appid; // 小程序ID
    private $wx_mini_secret; // 小程序app_secret密钥

    public $debug = false;

    public $errCode = 40001;

    public $errMsg = "no access";

    public function __construct(array $options)
    {
        $this->wx_mini_appid = isset($options['appid']) ? $options['appid'] : '';
        $this->wx_mini_secret = isset($options['secret']) ? $options['secret'] : '';
        $this->shop_id = isset($options['shop_id']) ? $options['shop_id'] : '';
    }


    /**
     * 换取 access_token
     * @param
     * @return
     */
    public function getAccessToken()
    {
        $paramJson = [
            'code' => '',
            'grant_type' => 'authorization_self',
            'test_shop' => '1',
            'shop_id' => $this->shop_id,//店铺id
            'auth_id' => '',
            'auth_subject_type' => 'WuLiuShang'
        ];
        $method = 'token.create';
        $timestamp = date('Y-m-d H:i:s',time());
        $v = 2;
        $paramJson = self::marshal($paramJson);
        $sign = self::getSign($method, $paramJson, $timestamp, $v);
        $methodPath = str_replace('.', '/', $method);
        $url = self::API_URL_PREFIX . '/' . $methodPath .
                '?method=' . urlencode($method) .
                '&app_key=' . urlencode($this->wx_mini_appid) .
                '&timestamp=' . urlencode(strval($timestamp)) .
                '&v=' . urlencode($v) .
                '&sign=' . urlencode($sign) .
                '&sign_method=' . urlencode('hmac-sha256');
        $opts = array('http' =>
            array(
                'method' => 'POST',
                'header' => "Accept: */*\r\n" .
                    "Content-type: application/json;charset=UTF-8\r\n",
                'content' => $paramJson
            )
        );
        $context = stream_context_create($opts);
        $result = file_get_contents($url, false, $context);
        if ($result) {
            $json = json_decode($result, true);
            if($json['code'] == '10000'){
                return $json['data']['access_token'];
            }else{
                return false;
            }            
        }
        return false;
    }

    /**
     * 获取类目
     * @param
     * @return
     */
    public function getShopCategory($paramJson = [])
    {
        $method = 'shop.getShopCategory';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }

    /**
     * 单个图片上传
     * @param
     * @return
     */
    public function uploadImageSync($paramJson = [])
    {
        $method = 'material.uploadImageSync';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }

    /**
     * 根据素材id查素材详情
     * @param
     * @return
     */
    public function queryMaterialDetail($paramJson = [])
    {
        $method = 'material.queryMaterialDetail';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }


    /**
     * 发布商品
     * @param
     * @return
     */
    public function productAddv2($paramJson = [])
    {
        $method = 'product.addV2';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }

    /**
     * 编辑商品
     * @param
     * @return
     */
    public function productEditV2($paramJson = [])
    {
        $method = 'product.editV2';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }

    /**
     * 查询商品发布规则
     * @param
     * @return
     */
    public function getProductUpdateRule($paramJson = [])
    {
        $method = 'product.getProductUpdateRule';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }

    /**
     * 查询商品类目id的属性列表
     * @param
     * @return
     */
    public function getCatePropertyV2($paramJson = [])
    {
        $method = 'product.getCatePropertyV2';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }

    /**
     * 订单列表查询
     * @param
     * @return
     */
    public function getOrdersearchList($paramJson = [])
    {
        $method = 'order.searchList';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }

    /**
     * 订单详情查询
     * @param
     * @return
     */
    public function getOrderorderDetail($paramJson = [])
    {
        $method = 'order.orderDetail';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }

    /**
     * 订单发货
     * @param
     * @return
     */
    public function getOrderlogisticsAdd($paramJson = [])
    {
        $method = 'order.logisticsAdd';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }

    /**
     * 商家处理换货请求接口
     * @param
     * @return
     */
    public function getAfterSalebuyerExchange($paramJson = [])
    {
        $method = 'afterSale.buyerExchange';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }

    /**
     * 获取快递公司列表
     * @param
     * @return
     */
    public function getOrderlogisticsCompanyList()
    {
        $method = 'order.logisticsCompanyList';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }

    /**
     * 批量解密接口
     * @param
     * @return
     */
    public function getOrderbatchDecrypt($paramJson = [])
    {
        $method = 'order.batchDecrypt';
        $result = self::comment($method, $paramJson);
        if ($result) {
            $json = json_decode($result, true);
            return $json; 
        }
        return false;
    }

    /**
     * 公共参数
     * @param
     * @return
     */
    protected function comment($method, $paramJson = [])
    {
        $timestamp = date('Y-m-d H:i:s',time());
        $v = 2;
        $access_token = self::getAccessToken();
        if($paramJson){
            $paramJson = self::marshal($paramJson);
        }else{
            $paramJson = '{}';
        }
        $sign = self::getSign($method, $paramJson, $timestamp, $v);
        $methodPath = str_replace('.', '/', $method);
        $url = self::API_URL_PREFIX . '/' . $methodPath .
                '?method=' . urlencode($method) .
                '&app_key=' . urlencode($this->wx_mini_appid) .
                '&timestamp=' . urlencode(strval($timestamp)) .
                '&access_token=' . urlencode($access_token) .
                '&v=' . urlencode($v) .
                '&sign=' . urlencode($sign) .
                '&sign_method=' . urlencode('hmac-sha256');
        $opts = array('http' =>
            array(
                'method' => 'POST',
                'header' => "Accept: */*\r\n" .
                    "Content-type: application/json;charset=UTF-8\r\n",
                'content' => $paramJson
            )
        );
        $context = stream_context_create($opts);
        $result = file_get_contents($url, false, $context);
        return $result;
    }

    /**
     * 换取 sign
     * @param
     * @return
     */
    public function getSign($method, $paramJson, $timestamp, $v)
    {
        $paramPattern = 'app_key' . $this->wx_mini_appid . 'method' . $method . 'param_json' . $paramJson . 'timestamp' . $timestamp . 'v' . $v; 
        $signPattern = $this->wx_mini_secret . $paramPattern . $this->wx_mini_secret; 
        $sign = hash_hmac("sha256", $signPattern, $this->wx_mini_secret);
        return $sign;
    }

    // 序列化参数，入参必须为关联数组
    public function marshal($param) {
        self::rec_ksort($param); // 对关联数组中的kv，执行排序，需要递归
        $s = json_encode($param, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); // 重新序列化，确保所有key按字典序排序
        // 加入flag，确保斜杠不被escape，汉字不被escape
        return $s;
    }

    // 关联数组排序，递归
    public function rec_ksort(&$arr) {
        $kstring = true;
        foreach ($arr as $k => &$v) {
            if (!is_string($k)) {
                $kstring = false;
            }
            if (is_array($v)) {
                self::rec_ksort($v);
            }
        }
        if ($kstring) {
            ksort($arr);
        }
    }


    /**
     * GET 请求
     * @param $url
     * @param int $timeout
     * @param string $header
     * @return bool|mixed
     */
    protected function curlGet($url, $timeout = 5, $header = "")
    {
        $ch = curl_init();
        if (stripos($url, "https://") !== false) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        curl_setopt($ch, CURLOPT_HTTP_VERSION, 1);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [$header]);//模拟的header头
        $result = curl_exec($ch);
        $aStatus = curl_getinfo($ch);
        curl_close($ch);
        if (intval($aStatus["http_code"]) == 200) {
            return $result;
        } else {
            return false;
        }
    }

    /**
     * POST 请求
     * @param $url
     * @param $post_data
     * @param int $timeout
     * @return bool|mixed
     */
    protected function curlPost($url, $post_data, $timeout = 5)
    {
        $ch = curl_init();
        if (stripos($url, "https://") !== false) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        $header = empty($header) ? '' : $header;
        if (is_string($post_data)) {
            $strPOST = $post_data;
        } else {
            $aPOST = [];
            foreach ($post_data as $key => $val) {
                $aPOST[] = $key . "=" . urlencode($val);
            }
            $strPOST = join("&", $aPOST);
        }
        curl_setopt($ch, CURLOPT_HTTP_VERSION, 1);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $strPOST);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        // curl_setopt($ch, CURLOPT_HTTPHEADER, array($header));//模拟的header头
        $result = curl_exec($ch);
        $aStatus = curl_getinfo($ch);
        curl_close($ch);
        if (intval($aStatus["http_code"]) == 200) {
            return $result;
        } else {
            return false;
        }
    }

    /**
     * 微信api不支持中文转义的json结构
     * @param $arr
     * @return string
     */
    public static function json_encode($arr)
    {
        if (count($arr) == 0) {
            return "[]";
        }
        $parts = [];
        $is_list = false;
        //Find out if the given array is a numerical array
        $keys = array_keys($arr);
        $max_length = count($arr) - 1;
        if (($keys [0] === 0) && ($keys [$max_length] === $max_length)) { //See if the first key is 0 and last key is length - 1
            $is_list = true;
            for ($i = 0; $i < count($keys); $i++) { //See if each key correspondes to its position
                if ($i != $keys [$i]) { //A key fails at position check.
                    $is_list = false; //It is an associative array.
                    break;
                }
            }
        }
        foreach ($arr as $key => $value) {
            if (is_array($value)) { //Custom handling for arrays
                if ($is_list) {
                    $parts [] = self::json_encode($value);
                } /* :RECURSION: */
                else {
                    $parts [] = '"' . $key . '":' . self::json_encode($value);
                } /* :RECURSION: */
            } else {
                $str = '';
                if (!$is_list) {
                    $str = '"' . $key . '":';
                }
                //Custom handling for multiple data types
                if (!is_string($value) && is_numeric($value) && $value < 2000000000) {
                    $str .= $value;
                } //Numbers
                elseif ($value === false) {
                    $str .= 'false';
                } //The booleans
                elseif ($value === true) {
                    $str .= 'true';
                } else {
                    $str .= '"' . addslashes($value) . '"';
                } //All other things
                // :TODO: Is there any more datatype we should be in the lookout for? (Object?)
                $parts [] = $str;
            }
        }
        $json = implode(',', $parts);
        if ($is_list) {
            return '[' . $json . ']';
        } //Return numerical JSON
        return '{' . $json . '}'; //Return associative JSON
    }

    /**
     * 日志记录
     * @param mixed $log 输入日志
     * @return mixed
     */
    public function log($log)
    {
        $log = is_array($log) ? var_export($log, true) : $log;
        if ($this->debug && function_exists('logResult')) {
            logResult($log);
        }
    }

    /**
     * 设置缓存
     * @param string $cachename
     * @param mixed $value
     * @param int $expired
     * @return boolean
     */
    protected function setCache($cachename, $value, $expired)
    {
        return S($cachename, $value, $expired);
    }

    /**
     * 获取缓存
     * @param string $cachename
     * @return mixed
     */
    protected function getCache($cachename)
    {
        return S($cachename);
    }

    /**
     * 清除缓存
     * @param string $cachename
     * @return boolean
     */
    protected function removeCache($cachename)
    {
        return S($cachename, null);
    }
}
